package com.hrma.services.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.hrma.dao.*;
import com.hrma.entity.*;
import com.hrma.entity.exception.DataIsNull;
import com.hrma.services.AttendanceService;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.sql.SQLException;
import java.util.*;

@Service
public class AttendanceImpl implements AttendanceService {
    private final AttendanceMapper attendanceMapper;
    private final EmployeeMapper employeeMapper;
    private final TravelApplicationMapper travelApplicationMapper;
    private final LeaveApplicationMapper leaveApplicationMapper;
    private final WorkScheduleMapper workScheduleMapper;
    private final OvertimeRecordMapper overtimeRecordMapper;

    public AttendanceImpl(AttendanceMapper attendanceMapper, EmployeeMapper employeeMapper, TravelApplicationMapper travelApplicationMapper, LeaveApplicationMapper leaveApplicationMapper, WorkScheduleMapper workScheduleMapper, OvertimeRecordMapper overtimeRecordMapper) {
        this.attendanceMapper = attendanceMapper;
        this.employeeMapper = employeeMapper;
        this.travelApplicationMapper = travelApplicationMapper;
        this.leaveApplicationMapper = leaveApplicationMapper;
        this.workScheduleMapper = workScheduleMapper;
        this.overtimeRecordMapper = overtimeRecordMapper;
    }


    @Override
    public void lastMonthAttendanceResult() {
        //上月第一天0点
        Calendar lastMonthFirstDateCal = Calendar.getInstance();
        lastMonthFirstDateCal.add(Calendar.MONTH, -1);
        lastMonthFirstDateCal.set(Calendar.MILLISECOND, 0);
        int year = lastMonthFirstDateCal.get(Calendar.YEAR);
        int month = lastMonthFirstDateCal.get(Calendar.MONTH);
        lastMonthFirstDateCal.set(year, month, 1, 0, 0, 0);
        Date lastMonthFirstDate = lastMonthFirstDateCal.getTime();
        //本月第一天0点
        Calendar lastMonthEndDateCal = Calendar.getInstance();
        lastMonthEndDateCal.set(Calendar.MILLISECOND, 0);
        int year1 = lastMonthEndDateCal.get(Calendar.YEAR);
        int month1 = lastMonthEndDateCal.get(Calendar.MONTH);
        lastMonthEndDateCal.set(year1, month1, 1, 0, 0, 0);
        Date lastMonthEndDate = lastMonthEndDateCal.getTime();

        //得到所有上月在职的员工编号
        List<String> allNum = employeeMapper.getAllNumByMonFirstDay(lastMonthFirstDate, lastMonthEndDate);
        //得到考勤时间表
        WorkSchedule workSchedule = workScheduleMapper.getTime();
        //将考勤时间表存储的整数转成时间类型
        WorkSchedulePO workSchedulePO = toPO(workSchedule);

        //统计结果存储对象
        AttendanceResult attendanceResult = new AttendanceResult();
        attendanceResult.setBeginTime(lastMonthFirstDate);
        attendanceResult.setEndTime(lastMonthEndDate);
        //循环统计每个员工的考勤结果
        for (String employeeNum : allNum) {
            attendanceResult.setEmployeeNum(employeeNum);
            //根据员工编号得到上月所有请假时间
            List<beginEndTime> leaveTime = leaveApplicationMapper.getBeginEndTime(employeeNum, lastMonthFirstDate, lastMonthEndDate);

            int leave = 0;
            int travel = 0;
            int attendance = 0;
            int late = 0;
            int leaveEarly = 0;
            int absence = 0;
            int overtime = 0;

            //循环上月每一天
            Date oneDay = lastMonthFirstDate;
            while (oneDay.getTime() < lastMonthEndDate.getTime()) {
                //得到当天打卡记录
                Calendar calendar1 = new GregorianCalendar();
                calendar1.setTime(oneDay);
                calendar1.add(Calendar.DATE, 1);
                List<Date> attendanceRecord = attendanceMapper.getOneDayAttendanceRecord(employeeNum, oneDay, calendar1.getTime());

                //统计当天加班情况
                overtime += overTimeCount(employeeNum,oneDay,attendanceRecord,workSchedulePO);

                //判断当天是否是周末
                Calendar cale = Calendar.getInstance();
                cale.setTime(oneDay);
                if (cale.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY || cale.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                    //下一天
                    lastMonthFirstDateCal.add(Calendar.DAY_OF_MONTH, 1);
                    oneDay = lastMonthFirstDateCal.getTime();
                    continue;
                }

                //判断当天是否请假,请假不足半天算半天,工作期间请假或工作期间结束请假都算半天请假
                boolean amLeavel = false;
                boolean pmLeavel = false;
                for (beginEndTime beginEnd : leaveTime) {
                    //整个上午期间都请假了,或上午工作期间开始请假,或上午工作期间结束请假
                    boolean allAmLeave = ((beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime()) && (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime()));
                    boolean amBeginLeave = (beginEnd.getBeginTime().getTime() >= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime() && beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime());
                    boolean amEndLeave = (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime() && beginEnd.getEndTime().getTime() <= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime());
                    if ((allAmLeave || amBeginLeave || amEndLeave) && !amLeavel) {
                        leave++;
                        amLeavel = true;
                    }

                    //整个下午期间都请假了,或下午工作期间开始请假,或下午工作期间结束请假
                    if ((((beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime()) && (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime())) || (beginEnd.getBeginTime().getTime() >= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime() && beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime()) || (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime() && beginEnd.getEndTime().getTime() <= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime())) && !pmLeavel) {
                        leave++;
                        pmLeavel = true;
                    }
                    if (amLeavel && pmLeavel) break;
                }


                //全天都请假了直接下一天
                if (amLeavel && pmLeavel) {
                    //下一天
                    lastMonthFirstDateCal.add(Calendar.DAY_OF_MONTH, 1);
                    oneDay = lastMonthFirstDateCal.getTime();
                    continue;
                }

                //根据员工编号获取出差时间
                List<beginEndTime> travelTime = travelApplicationMapper.getBeginEndTime(employeeNum, lastMonthFirstDate, lastMonthEndDate);

                boolean amTravel = false;
                boolean pmTravel = false;

                //上午没请假
                if (!amLeavel) {
                    //上午是否出差,足半天算半天
                    for (beginEndTime beginEnd : travelTime) {
                        //整个上午期间都出差了,或者上午工作期间开始出差或结束出差
                        if (((beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime()) && (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime())) || (beginEnd.getBeginTime().getTime() >= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime() && beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime()) || (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime() && beginEnd.getEndTime().getTime() <= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime())) {
                            travel++;
                            amTravel = true;
                            break;
                        }
                    }
                    //未出差
                    if (!amTravel) {
                        boolean commonAttendance = false;
                        boolean lateAttendance = false;
                        boolean commonLeave = false;
                        boolean leaveEarlyAttendance = false;
                        //是否正常打卡上班
                        for (Date i : attendanceRecord) {
                            if (i.getTime() >= timeToDate(oneDay, workSchedulePO.getAmBeginTime()).getTime() && i.getTime() <= timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime()) {
                                commonAttendance = true;
                                break;
                            }
                        }
                        //未正常打卡上班,判断是否迟到
                        if (!commonAttendance) {
                            for (Date i : attendanceRecord) {
                                if (i.getTime() > timeToDate(oneDay, workSchedulePO.getAmBeginWork()).getTime() && i.getTime() <= timeToDate(oneDay, workSchedulePO.getAmEndTime()).getTime()) {
                                    lateAttendance = true;
                                    break;
                                }
                            }
                        }

                        //早上是否有打卡记录
                        if (commonAttendance || lateAttendance) {
                            //是否正常下班
                            for (Date i : attendanceRecord) {
                                if (i.getTime() >= timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime() && i.getTime() <= timeToDate(oneDay, workSchedulePO.getMidEndTime()).getTime()) {
                                    attendance++;
                                    commonLeave = true;
                                    if (lateAttendance) {
                                        late++;
                                    }
                                    break;
                                }
                            }
                            //未正常下班,是早退还是旷工
                            if (!commonLeave) {
                                for (Date i : attendanceRecord) {
                                    if (i.getTime() >= timeToDate(oneDay, workSchedulePO.getMidBeginTime()).getTime() && i.getTime() < timeToDate(oneDay, workSchedulePO.getAmEndWork()).getTime()) {
                                        attendance++;
                                        leaveEarly++;
                                        leaveEarlyAttendance = true;
                                        if (lateAttendance) {
                                            late++;
                                        }
                                        break;
                                    }
                                }
                                if (!leaveEarlyAttendance) {
                                    absence++;
                                }
                            }

                        } else {
                            absence++;
                        }

                    }
                }
                //下午没请假
                if (!pmLeavel) {
                    //下午是否出差,不足半天算半天
                    for (beginEndTime beginEnd : travelTime) {
                        //整个下午期间都出差了,或者下午工作期间开始出差或结束出差
                        if (((beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime()) && (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime())) || (beginEnd.getBeginTime().getTime() >= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime() && beginEnd.getBeginTime().getTime() <= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime()) || (beginEnd.getEndTime().getTime() >= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime() && beginEnd.getEndTime().getTime() <= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime())) {
                            travel++;
                            pmTravel = true;
                            break;
                        }
                    }
                    //未出差
                    if (!pmTravel) {
                        boolean commonAttendance = false;
                        boolean lateAttendance = false;
                        boolean commonLeave = false;
                        boolean leaveEarlyAttendance = false;
                        //是否正常打卡上班
                        for (Date i : attendanceRecord) {
                            if (i.getTime() >= timeToDate(oneDay, workSchedulePO.getPmBeginTime()).getTime() && i.getTime() <= timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime()) {
                                commonAttendance = true;
                                break;
                            }
                        }
                        //未正常打卡上班,判断是否迟到
                        if (!commonAttendance) {
                            for (Date i : attendanceRecord) {
                                if (i.getTime() > timeToDate(oneDay, workSchedulePO.getPmBeginWork()).getTime() && i.getTime() <= timeToDate(oneDay, workSchedulePO.getPmEndTime()).getTime()) {
                                    lateAttendance = true;
                                    break;
                                }
                            }
                        }

                        //下午上班是否有打卡记录
                        if (commonAttendance || lateAttendance) {
                            //是否正常下班
                            for (Date i : attendanceRecord) {
                                if (i.getTime() >= timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime() && i.getTime() <= timeToDate(oneDay, workSchedulePO.getEmEndTime()).getTime()) {
                                    attendance++;
                                    commonLeave = true;
                                    if (lateAttendance) {
                                        late++;
                                    }
                                    break;
                                }
                            }
                            //未正常下班,是早退还是旷工
                            if (!commonLeave) {
                                for (Date i : attendanceRecord) {
                                    if (i.getTime() >= timeToDate(oneDay, workSchedulePO.getEmBeginTime()).getTime() && i.getTime() < timeToDate(oneDay, workSchedulePO.getPmEndWork()).getTime()) {
                                        attendance++;
                                        leaveEarly++;
                                        leaveEarlyAttendance = true;
                                        if (lateAttendance) {
                                            late++;
                                        }
                                        break;
                                    }
                                }
                                if (!leaveEarlyAttendance) {
                                    absence++;
                                }
                            }

                        } else {
                            absence++;
                        }
                    }
                }

                //统计加班打卡情况

                //下一天
                lastMonthFirstDateCal.add(Calendar.DAY_OF_MONTH, 1);
                oneDay = lastMonthFirstDateCal.getTime();
            }

            attendanceResult.setLate(late);
            attendanceResult.setLeaveEarly(leaveEarly);
            attendanceResult.setAbsence(absence);
            attendanceResult.setLeaveNum(leave);
            attendanceResult.setTravelNum(travel);
            attendanceResult.setWorkNum(attendance);
            attendanceResult.setOvertime(overtime);
            attendanceMapper.addAttendanceStatistic(attendanceResult);
        }
    }

    @Override
    public void add(AttendanceResult data) {
        attendanceMapper.addAttendanceStatistic(data);
    }

    @Override
    public void delete(int id) {
        attendanceMapper.deleteOne(id);
    }

    @Override
    public void update(AttendanceResult data) {
        attendanceMapper.update(data);
    }

    @Override
    public List<SelectResult> select() {
        return attendanceMapper.select();
    }

    @Override
    public Page<Attendance> getList(AttendanceDTO data) {
        PageHelper.startPage(data.getPageNum(), data.getPageSize());
        return attendanceMapper.getList(data);
    }

    @Override
    public AttendanceRule getRule() {
        return attendanceMapper.getAttendanceRule();
    }

    @Override
    public void updateRule(AttendanceRule data) {
        attendanceMapper.deleteRule();
        attendanceMapper.addRule(data);
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, SQLException.class})
    public void handleUpload(MultipartFile multipartFile) throws IOException {
        //清空以前临时存储的考勤记录
        attendanceMapper.delete();

        //导入考勤记录excel表
        Workbook wb = new XSSFWorkbook(multipartFile.getInputStream());
        Sheet sheet = wb.getSheetAt(0);
        for (int j = 1; true; j++) {
            Row row = sheet.getRow(j);
            if (row == null) break;
            String employeeNum = row.getCell(0).getStringCellValue();
            Date time = row.getCell(1).getDateCellValue();

            AttendancePO attendancePO = new AttendancePO();
            attendancePO.setEmployeeNum(employeeNum);
            attendancePO.setTime(time);
            attendanceMapper.addAttendance(attendancePO);

            if (("".equals(employeeNum)) || (time == null) || (employeeNum == null)) {
                throw new DataIsNull("第" + (j + 1) + "行数据缺失!");
            }
        }

        //导入打卡记录后统计结果
        lastMonthAttendanceResult();

    }

    @Override
    public void delete() {
        attendanceMapper.delete();
    }


    /**
     * 将工作时间表的年月日转成上月循环当天的年月日
     */
    public Date timeToDate(Date oneDay, Date workScheduleTime) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(oneDay);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(workScheduleTime);
        int hour = calendar1.get(Calendar.HOUR_OF_DAY);
        int min = calendar1.get(Calendar.MINUTE);
        int sec = calendar1.get(Calendar.SECOND);
        calendar.set(year, month, day, hour, min, sec);
        return calendar.getTime();
    }

    /**
     * 将数据库存的工作时间整数转成本地时间类型
     */
    public WorkSchedulePO toPO(WorkSchedule workSchedule) {
        //创建结果存储对象
        WorkSchedulePO workSchedulePO = new WorkSchedulePO();

        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);

        calendar.set(year, month, day, workSchedule.getAmBeginTimeHour(), workSchedule.getAmBeginTimeMin(), 0);
        workSchedulePO.setAmBeginTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getAmBeginWorkHour(), workSchedule.getAmBeginWorkMin(), 0);
        workSchedulePO.setAmBeginWork(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getAmEndTimeHour(), workSchedule.getAmEndTimeMin(), 0);
        workSchedulePO.setAmEndTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getMidBeginTimeHour(), workSchedule.getMidBeginTimeMin(), 0);
        workSchedulePO.setMidBeginTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getAmEndWorkHour(), workSchedule.getAmEndWorkMin(), 0);
        workSchedulePO.setAmEndWork(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getMidEndTimeHour(), workSchedule.getMidEndTimeMin(), 0);
        workSchedulePO.setMidEndTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getPmBeginTimeHour(), workSchedule.getPmBeginTimeMin(), 0);
        workSchedulePO.setPmBeginTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getPmBeginWorkHour(), workSchedule.getPmBeginWorkMin(), 0);
        workSchedulePO.setPmBeginWork(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getPmEndTimeHour(), workSchedule.getPmEndTimeMin(), 0);
        workSchedulePO.setPmEndTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getEmBeginTimeHour(), workSchedule.getEmBeginTimeMin(), 0);
        workSchedulePO.setEmBeginTime(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getPmEndWorkHour(), workSchedule.getPmEndWorkMin(), 0);
        workSchedulePO.setPmEndWork(calendar.getTime());
        calendar.set(year, month, day, workSchedule.getEmEndTimeHour(), workSchedule.getEmEndTimeMin(), 0);
        workSchedulePO.setEmEndTime(calendar.getTime());

        return workSchedulePO;
    }

    /**
     * 根据员工编号和时间统计当天加班几小时
     */
    public int overTimeCount(String employeeNum, Date oneDay, List<Date> attendanceRecord, WorkSchedulePO workSchedulePO) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(oneDay);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        calendar.set(year, month, day + 1, 0, 0, 0);
        Date secondDay = calendar.getTime();
        List<beginEndTime> overtimeBeginEnd = overtimeRecordMapper.getBeginEndTime(employeeNum, oneDay, secondDay);

        long overtimeCount = 0;
        //加班只要当天加班期间有一次打卡就算正常加班
        for (beginEndTime i : overtimeBeginEnd) {
            for (Date recordTime : attendanceRecord) {
                if (recordTime.getTime() >= oneDay.getTime() && recordTime.getTime() <= i.getEndTime().getTime()) {
                    overtimeCount = (i.getEndTime().getTime() - i.getBeginTime().getTime()) / (1000 * 60 * 60);
                    //大于半小时但不足一小时算一小时
                    if(((i.getEndTime().getTime() - i.getBeginTime().getTime()) % (1000 * 60 * 60)) >= 0.5){
                        overtimeCount++;
                    }
                    break;
                }
            }

        }
        return (int)overtimeCount;
    }

}
